Add fake MQTT server from ruby-mqtt gem

Daniel O'Connor 10 ans auparavant
Parent
Commettre
325d2eace6
1 fichiers modifiés avec 131 ajouts et 0 suppressions
  1. 131 0
      spec/support/fake_mqtt_server.rb

+ 131 - 0
spec/support/fake_mqtt_server.rb

@@ -0,0 +1,131 @@
1
+#!/usr/bin/env ruby
2
+#
3
+# This is a 'fake' MQTT server to help with testing client implementations
4
+#
5
+# See https://github.com/njh/ruby-mqtt/blob/master/spec/fake_server.rb
6
+#
7
+# It behaves in the following ways:
8
+#   * Responses to CONNECT with a successful CONACK
9
+#   * Responses to PUBLISH by echoing the packet back
10
+#   * Responses to SUBSCRIBE with SUBACK and a PUBLISH to the topic
11
+#   * Responses to PINGREQ with PINGRESP
12
+#   * Responses to DISCONNECT by closing the socket
13
+#
14
+# It has the following restrictions
15
+#   * Doesn't deal with timeouts
16
+#   * Only handles a single connection at a time
17
+#
18
+
19
+$:.unshift File.dirname(__FILE__)+'/../lib'
20
+
21
+require 'logger'
22
+require 'socket'
23
+require 'mqtt'
24
+
25
+
26
+class MQTT::FakeServer
27
+  attr_reader :address, :port
28
+  attr_reader :last_publish
29
+  attr_reader :thread
30
+  attr_reader :pings_received
31
+  attr_accessor :just_one
32
+  attr_accessor :logger
33
+
34
+  # Create a new fake MQTT server
35
+  #
36
+  # If no port is given, bind to a random port number
37
+  # If no bind address is given, bind to localhost
38
+  def initialize(port=nil, bind_address='127.0.0.1')
39
+    @port = port
40
+    @address = bind_address
41
+  end
42
+
43
+  # Get the logger used by the server
44
+  def logger
45
+    @logger ||= Logger.new(STDOUT)
46
+  end
47
+
48
+  # Start the thread and open the socket that will process client connections
49
+  def start
50
+    @socket ||= TCPServer.new(@address, @port)
51
+    @address = @socket.addr[3]
52
+    @port = @socket.addr[1]
53
+    @thread ||= Thread.new do
54
+      logger.info "Started a fake MQTT server on #{@address}:#{@port}"
55
+      loop do
56
+        # Wait for a client to connect
57
+        client = @socket.accept
58
+        @pings_received = 0
59
+        handle_client(client)
60
+        break if just_one
61
+      end
62
+    end
63
+  end
64
+
65
+  # Stop the thread and close the socket
66
+  def stop
67
+    logger.info "Stopping fake MQTT server"
68
+    @socket.close unless @socket.nil?
69
+    @socket = nil
70
+
71
+    @thread.kill if @thread and @thread.alive?
72
+    @thread = nil
73
+  end
74
+
75
+  # Start the server thread and wait for it to finish (possibly never)
76
+  def run
77
+    start
78
+    begin
79
+      @thread.join
80
+    rescue Interrupt
81
+      stop
82
+    end
83
+  end
84
+
85
+
86
+  protected
87
+
88
+  # Given a client socket, process MQTT packets from the client
89
+  def handle_client(client)
90
+    loop do
91
+      packet = MQTT::Packet.read(client)
92
+      logger.debug packet.inspect
93
+
94
+      case packet
95
+        when MQTT::Packet::Connect
96
+          client.write MQTT::Packet::Connack.new(:return_code => 0)
97
+        when MQTT::Packet::Publish
98
+          client.write packet
99
+          @last_publish = packet
100
+        when MQTT::Packet::Subscribe
101
+          client.write MQTT::Packet::Suback.new(
102
+            :message_id => packet.message_id,
103
+            :granted_qos => 0
104
+          )
105
+          topic = packet.topics[0][0]
106
+          client.write MQTT::Packet::Publish.new(
107
+            :topic => topic,
108
+            :payload => "hello #{topic}",
109
+            :retain => true
110
+          )
111
+        when MQTT::Packet::Pingreq
112
+          client.write MQTT::Packet::Pingresp.new
113
+          @pings_received += 1
114
+        when MQTT::Packet::Disconnect
115
+          client.close
116
+        break
117
+      end
118
+    end
119
+
120
+    rescue MQTT::ProtocolException => e
121
+      logger.warn "Protocol error, closing connection: #{e}"
122
+      client.close
123
+  end
124
+
125
+end
126
+
127
+if __FILE__ == $0
128
+  server = MQTT::FakeServer.new(MQTT::DEFAULT_PORT)
129
+  server.logger.level = Logger::DEBUG
130
+  server.run
131
+end